<?php

namespace IYUU\sdk\CoolQ\Modules\system\Domain;

use BadRequestException;
use BasicReplyModel;
use IYUU\sdk\CoolQ\Context;
use IYUU\sdk\CoolQ\Event\Message as EventMessage;
use IYUU\sdk\CoolQ\ModulesDomainInterface;
use RuleKeywordModel;
use RuleModel;
use think\db\exception\DataNotFoundException;
use think\db\exception\DbException;
use think\db\exception\ModelNotFoundException;
use think\facade\Db;
use function IYUU\sdk\CoolQ\verifyMatchType;

/**
 * 基本回复词库管理
 * - 增、删、查
 * Class BasicReply
 * @package IYUU\sdk\CoolQ\Modules\system\Domain
 */
class BasicReply implements ModulesDomainInterface
{
    /**
     * 命令分隔符
     */
    const SEP = '{##}';
    /**
     * @var array
     */
    private $data = [];

    /**
     * 插件状态
     * @return bool
     */
    public function state(): bool
    {
        return true;
    }

    /**
     * 菜单
     * @param string $message 消息内容
     * @return string
     */
    public function menu(string $message): string
    {
        if ($this->keyword() === $message) {
            $reply = '词库操作格式：' . PHP_EOL;
            $reply .= '创建规则及关键字：new{分隔符}规则名{分隔符}关键字{分隔符}匹配模式' . PHP_EOL;
            $reply .= '编辑应答：reply{分隔符}规则rid{分隔符}回复内容' . PHP_EOL;
            //$reply.= '开关：status{分隔符}主键{分隔符}1' . PHP_EOL;
            $reply .= '查规则：rule{分隔符}页码' . PHP_EOL;
            $reply .= '查关键字：show{分隔符}页码' . PHP_EOL;
            $reply .= '编辑关键词：edit{分隔符}主键{分隔符}关键字{分隔符}匹配模式' . PHP_EOL;
            $reply .= '增加关键词：add{分隔符}规则rid{分隔符}关键字{分隔符}匹配模式' . PHP_EOL . PHP_EOL;
            $reply .= '匹配模式：1相等、2包含、3正则';
            return str_replace('{分隔符}', BasicReply::SEP, $reply);
        }

        return '';
    }

    /**
     * 触发的关键字
     * @return string
     */
    public function keyword(): string
    {
        return '词库';
    }

    /**
     * @param Context $context
     * @param EventMessage $msgObj
     * @return string
     * @throws BadRequestException
     * @throws DataNotFoundException
     * @throws DbException
     * @throws ModelNotFoundException
     */
    public function process(Context $context, EventMessage $msgObj): string
    {
        // 必须工作人员，才能进行的操作
        $msgObj->requireWorker();
        $message = $context->get('message');
        if (empty($message)) {
            return '命令为空';
        }

        // 分隔命令
        if (strpos($message, self::SEP) !== false) {
            $this->data = explode(self::SEP, $message);
        } else {
            return '格式不正确';
        }

        // 路由
        switch (true) {
            case (strpos($message, 'new' . self::SEP) === 0):
                return $this->new();
            case (strpos($message, 'reply' . self::SEP) === 0):
                return $this->reply($msgObj);
            case (strpos($message, 'rule' . self::SEP) === 0):
                return $this->rule();
            case (strpos($message, 'show' . self::SEP) === 0):
                return $this->show();
            case (strpos($message, 'edit' . self::SEP) === 0):
                return $this->edit($msgObj);
            case (strpos($message, 'add' . self::SEP) === 0):
                return $this->add();
        }

        return '未匹配到命令路由';
    }

    /**
     * 创建规则、关键字、回复 内容
     * @return string
     */
    private function new(): string
    {
        // 1. 验证格式
        $ret = $this->verifyNew();
        if (is_string($ret)) {
            return $ret;
        }

        // 2. BOT主键 {cq_bot}表
        $bot_key = 1;

        // 3. 规则名 {cq_rule}表
        $name = $this->data[1];

        // 4. 规则关键字、匹配模式 {cq_rule_keyword}表、权限等级
        $content = $this->data[2] ?? $name;
        $match_type = $this->data[3] ?? 1;
        $auth_level = $this->data[4] ?? 0;

        // 5. 是否存在同名规则
        $rule = RuleModel::where('bot_key', $bot_key)->where('name', $name)->findOrEmpty();
        if (!$rule->isEmpty()) {
            return '错误：存在同名规则，主键:' . $rule->id;
        }

        // 6. 是否存在同名的关键字
        $rule_keyword = RuleKeywordModel::where('bot_key', $bot_key)->where('content', $content)->findOrEmpty();
        if (!$rule_keyword->isEmpty()) {
            return '错误：存在同名规则关键字，主键:' . $rule_keyword->id;
        }

        /**
         * 数据库事务
         */
        [$rid, $keyword_id, $reply_id] = Db::transaction(function () use ($bot_key, $name, $content, $match_type, $auth_level) {
            // 7. 新增规则
            $data = [
                'bot_key' => $bot_key,
                'name' => trim($name),
                'module' => 'basic',
                'auth_level' => (int)$auth_level,
            ];
            $rule = RuleModel::create($data);
            $rid = $rule->id;

            // 8. 新增规则关键字
            $data = [
                'bot_key' => $bot_key,
                'rid' => $rid,
                'module' => 'basic',
                'content' => trim($content),
                'match_type' => $match_type,
                'auth_level' => (int)$auth_level,
            ];
            $rule_keyword = RuleKeywordModel::create($data);
            $keyword_id = $rule_keyword->id;

            // 9. 插入一条默认回复
            $data = [
                'rid' => $rid,
                'content' => '更新此回复，请输入命令：reply' . static::SEP . $rid . static::SEP . '回复内容',
                'auth_level' => (int)$auth_level,
            ];
            $basic_reply = BasicReplyModel::create($data);
            $reply_id = $basic_reply->id;

            return [$rid, $keyword_id, $reply_id];
        });

        // 10. 返回结果，等待进一步操作
        $resp = '增加成功，规则RID:' . $rid . PHP_EOL;
        $resp .= '更新默认的回复内容。' . PHP_EOL . PHP_EOL;
        $resp .= 'reply' . static::SEP . $rid . static::SEP . '回复内容';
        return $resp;
    }

    /**
     * 验证命令格式
     * @return string|bool
     */
    private function verifyNew()
    {
        // 数组长度
        if (count($this->data) < 4) {
            return '格式不正确，数组小于4';
        }
        $data = array_filter($this->data, function ($v, $k) {
            return $v !== '';
        }, ARRAY_FILTER_USE_BOTH);
        if (count($data) !== count($this->data)) {
            return '格式不正确，数组值不能为空';
        }

        // 匹配模式
        if (!is_numeric($this->data[3])) {
            return '匹配模式非数字：' . $this->data[3];
        } else {
            $this->data[3] = (int)$this->data[3];
        }
        if (!verifyMatchType($this->data[3])) {
            return '匹配模式：1相等,2包含,3正则' . PHP_EOL . '不正确的：' . $this->data[3];
        }
        return true;
    }

    /**
     * 编辑应答内容
     * - 使用唯一的rid更新
     * @param EventMessage $msgObj
     * @return string
     * @throws BadRequestException
     */
    private function reply(EventMessage $msgObj): string
    {
        //1. 验证格式
        $ret = $this->verify();
        if (is_string($ret)) {
            return $ret;
        }

        //2. 使用唯一的rid更新
        $rid = $this->data[1];
        $basic = BasicReplyModel::where('rid', $rid)->findOrEmpty();
        if ($basic->isEmpty()) {
            return sprintf('错误：不存在rid为%d的应答内容', $rid);
        }

        //3. 检查等级权限
        if (BasicReplyModel::field_auth_level['worker'] < $basic->auth_level) {
            $msgObj->requireMaster();
        }

        //4. 更新
        $basic->content = trim($this->data[2]);
        $basic->save();

        return '成功：更新应答内容!';
    }

    /**
     * 验证命令的格式
     * @return bool|string
     */
    private function verify()
    {
        if (count($this->data) < 3) {
            return '格式不正确，数组小于3';
        }
        $data = array_filter($this->data, function ($v, $k) {
            return $v !== '';
        }, ARRAY_FILTER_USE_BOTH);
        if (count($data) != count($this->data)) {
            return '格式不正确，数组值不能为空';
        }

        // 主键
        if (!is_numeric($this->data[1])) {
            return '主键非数字：' . $this->data[1];
        } else {
            $this->data[1] = (int)$this->data[1];
        }

        return true;
    }

    /**
     * 显示规则（支持分页）
     * - cq_rule数据表
     * @return string
     * @throws DataNotFoundException
     * @throws DbException
     * @throws ModelNotFoundException
     */
    private function rule(): string
    {
        $page = $this->filterPage($this->data);
        if (is_string($page)) {
            return $page;
        }

        $rule = RuleModel::where('module', '=', 'basic')->page($page, 10)->select();
        if ($rule->isEmpty()) {
            return '暂无规则';
        }
        $data = [];
        /**
         * @var int $index
         * @var RuleModel $item
         */
        foreach ($rule as $index => $item) {
            $data[] = sprintf('%d、%s（%s）', $item->id, $item->name, 0 === $item->status ? 'OFF' : 'ON');
        }
        return implode(PHP_EOL, $data);
    }

    /**
     * 过滤并得到页码
     * @return int|string
     */
    private function filterPage(array $data)
    {
        if (2 < count($data)) {
            return '格式错误，数组大于2';
        }

        [$cmd, $page] = $data;
        if (empty($page)) {
            $page = 1;
        } else {
            if (is_numeric($page)) {
                $page = abs((int)$page);
            } else {
                return '页码非数字';
            }
        }

        return $page;
    }

    /**
     * 显示关键字（支持分页）
     * - cq_rule_keyword数据表
     * @return string
     * @throws DataNotFoundException
     * @throws DbException
     * @throws ModelNotFoundException
     */
    private function show(): string
    {
        $page = $this->filterPage($this->data);
        if (is_string($page)) {
            return $page;
        }

        $rule = RuleKeywordModel::where('module', '=', 'basic')->page($page, 10)->select();
        if ($rule->isEmpty()) {
            return '暂无关键字';
        }
        $data = [];
        /**
         * @var int $index
         * @var RuleKeywordModel $item
         */
        foreach ($rule as $index => $item) {
            $data[] = sprintf('%d、%s（%s）%s', $item->id, $item->content, 0 === $item->status ? 'OFF' : 'ON', $item->match_type_text);
        }
        return implode(PHP_EOL, $data);
    }

    /**
     * 编辑关键字
     * - 使用唯一的id更新
     * @param EventMessage $msgObj
     * @return string
     * @throws BadRequestException
     */
    private function edit(EventMessage $msgObj): string
    {
        //1. 验证格式
        $ret = $this->verify();
        if (is_string($ret)) {
            return $ret;
        }
        [$cmd, $id, $content, $match_type] = $this->data;

        //2. 使用唯一的id更新
        $model = RuleKeywordModel::where('id', '=', $id)->where('module', '=', 'basic')->findOrEmpty();
        if ($model->isEmpty()) {
            return sprintf('错误：不存在主键为%d的关键字', $id);
        }

        //3. 检查等级权限
        if (RuleKeywordModel::field_auth_level['worker'] < $model->auth_level) {
            $msgObj->requireMaster();
        }

        //4. 更新
        $model->content = trim($content);
        if ($match_type && verifyMatchType((int)$match_type)) {
            $model->match_type = $match_type;
        }
        $model->save();

        return '成功：编辑关键字!';
    }

    /**
     * 增加关键字
     * - 使用唯一的id更新
     * @return string
     */
    private function add(): string
    {
        //1. 验证格式
        $ret = $this->verify();
        if (is_string($ret)) {
            return $ret;
        }

        //2. BOT主键 {cq_bot}表
        $bot_key = 1;

        [$cmd, $rid, $content, $match_type] = $this->data;

        //3. 检查规则
        $rule = RuleModel::where('id', $rid)->findOrEmpty();
        if ($rule->isEmpty()) {
            return sprintf('错误：不存在主键为%d的规则', $rid);
        }

        //4. 检查应答内容
        $basic = BasicReplyModel::where('rid', $rid)->findOrEmpty();
        if ($basic->isEmpty()) {
            return sprintf('错误：不存在rid为%d的应答内容', $rid);
        }

        //5. 新增关键字
        $data = [
            'bot_key' => $bot_key,
            'rid' => $rid,
            'module' => 'basic',
            'content' => trim($content),
            'match_type' => $match_type ?? 2,
        ];
        $rule_keyword = RuleKeywordModel::create($data);

        return '成功：增加关键字!主键：' . $rule_keyword->id;
    }
}
